Who Is This Course For?
Built for manual testers who know REST API concepts and have used Postman, and now want to write Python code that automates API testing end-to-end — with a professional framework, multi-environment config, and Allure reports.
MODULE 01
API Testing Fundamentals
REST · HTTP methods · status codes · Postman · request anatomy · API vs UI testing
REST API Concepts
- Client-Server architecture — request and response cycle
- HTTP methods:
GET,POST,PUT,PATCH,DELETE - Status codes —
200,201,401,404,422,500 - Request anatomy: URL, headers, body, query params
- REST vs SOAP — when to use each
- JSON structure — objects, arrays, nested keys
API Testing in Practice
- API tests are faster than UI — 2,000 scenarios run in minutes, not hours
- API testing in the test pyramid — contract, functional, integration layers
- Postman — explore and manually test APIs before automating
- Import collections, set environment variables in Postman
MODULE 02
Python & Environment Setup
Python 3 · PyCharm · pip · venv · requests library · response.json()
Python & IDE Setup
- Install Python 3.x — understand major vs minor versions
- PyCharm Community Edition — create and manage a Python project
- Virtual environment (
venv) — isolated library space per project pip install requests— install the requests library
First API Call
response = requests.get(url)— send your first GETresponse.status_code,response.json(),response.headers- Parse and print nested JSON values
- Python dict, list, f-string essentials for API testing
MODULE 03
GET Requests — Query Params, Path Params & Pagination
params dict · path params · f-string URL · pagination · json.dumps()
Query Parameters
- Pass as Python dict:
params={"page": 1, "per_page": 10} - Multiple query params — all in one dict, requests handles encoding
- Pagination —
pageandper_pageto control results - Read total record count from response headers
Path Parameters & JSON Navigation
- Path param in URL:
f"{base_url}/users/{username}/repos" - Difference between path param and query param
- Navigate nested JSON:
response.json()["key"]["subkey"] - Loop through JSON array and extract specific fields
- Pretty print:
json.dumps(data, indent=4)
MODULE 04
POST, PUT, PATCH & DELETE Requests
requests.post · json=payload · file upload · requests.patch · requests.delete · CRUD
POST Request
requests.post(url, json=payload, headers=headers)- Payload as Python
dict— auto-serialised to JSON - POST with file:
files={"file": open("file.csv","rb")} - Expected response:
201 Created
PUT, PATCH & DELETE
requests.put()— full replacement of a resourcerequests.patch()— partial update, only specified fields change- PUT vs PATCH — which to use and why
requests.delete(url, headers=headers)→204 No Content- Verify delete with a follow-up GET — assert
404 - Full CRUD flow: Create → Read → Update → Delete in one sequence
MODULE 05
Authentication & Headers
Bearer token · Authorization header · GitHub PAT · Basic auth · rate limiting
HTTP Headers
- Pass headers as dict to every request
Content-Type: application/jsonfor POST/PATCH bodyAcceptheader — tell server what format you expect back- Custom headers — e.g.
X-GitHub-Api-Version
Authentication
- Bearer token:
"Authorization": "Bearer <token>" - GitHub Personal Access Token (PAT) — generate, scope, and use
- Basic Auth:
requests.get(url, auth=("user","pass")) - Unauthenticated vs authenticated rate limits on GitHub API
- Handle rate limit:
X-RateLimit-Remainingheader - Negative test — wrong token → assert
401 Unauthorized
MODULE 06
Response Assertions & JSON Validation
assert · status code · body keys · sorted() · positive/negative · AssertionError
Status Code Assertions
assert response.status_code == 200- Custom failure message:
assert x == y, "Expected 200 got 401" - Positive test: valid token →
200; negative: no token →401
Response Body Validation
assert data["login"] == "expected_username"assert "id" in data— key existence checkassert len(data) == expected_countsorted(actual) == sorted(expected)— order-independent comparisonassert data["private"] == True— boolean field check- Positive and negative scenarios for each API endpoint
MODULE 07
GitHub REST API — Real-World Practice
GET /user · POST /user/repos · PATCH · DELETE · private/public · error scenarios
GitHub API Endpoints
GET /user— authenticated user profileGET /users/{username}/repos— list public repositoriesGET /user/repos?type=private— list private repos (auth required)POST /user/repos— create repo (name, private: true/false)PATCH /repos/{owner}/{repo}— update name/descriptionDELETE /repos/{owner}/{repo}— delete repo
Test Scenarios
- Create private repo → assert
201andprivate: true - Create public repo → assert
private: false - Duplicate repo name → assert
422 Unprocessable - No auth on write → assert
401 Unauthorized - Full CRUD cycle as one end-to-end sequence
MODULE 08
pytest Framework Introduction
@pytest.mark · conftest.py · @pytest.fixture · @pytest.mark.parametrize · pytest -m smoke
pytest Basics
- Test discovery:
test_*.pyfiles,def test_*()functions - Run all:
pytest| Run file:pytest test_github.py - Verbose:
pytest -v— see each test name and result
Markers, Fixtures & Parametrize
@pytest.mark.smoke,@pytest.mark.p1,@pytest.mark.p2- Run tagged subset:
pytest -m smoke— only smoke tests execute - Register markers in
pytest.inito avoid warnings @pytest.mark.parametrize— one test, multiple data setsconftest.py— shared fixtures, no imports needed@pytest.fixturewithyield— setup before, teardown after
MODULE 09
Framework Architecture — Base Class & Config
BaseTest · base_url · get_headers() · fixture scope · session scope · helpers
Base Class Design
BaseTestclass —base_urland headers in one place- Test classes inherit:
class TestGetRepos(BaseTest) - Helper method:
get_headers()builds auth headers automatically - One change in
BaseTestpropagates to all test classes instantly
conftest Fixtures & Helpers
- Fixture scope —
function,class,module,session - Session-scoped auth fixture — token loaded once, reused everywhere
- Utility functions: response validator, JSON extractor
- Project structure:
tests/,utils/,config/,conftest.py
MODULE 10
Environment Management with config.ini
configparser · [global] · [staging] · [production] · os.path · zero code change switch
config.ini Structure
[global]section withenvironment = staging[staging],[production],[dev]— each with ownbase_url- Token stored per environment section
Reading Config in Python
import configparserenv = config["global"]["environment"]base_url = config[env]["base_url"]- Resolve config path with
os.path— works on any machine - Change one line → entire framework points to a different environment
MODULE 11
Test Reporting — Allure & HTML Reports
allure-pytest · @allure.step · pytest-html · shutil.rmtree · fixture teardown cleanup
Allure Reports
pip install allure-pytest- Run with results:
pytest --alluredir=allure-results - Serve report:
allure serve allure-results @allure.step("Step label")— readable step names in report@allure.title(),@allure.description()decorators- Attach response body as report attachment
Report Management & Data Cleanup
pip install pytest-html→pytest --html=report.html- Delete stale report folder:
shutil.rmtree("allure-results") - Delete test data in fixture teardown with
yield— cleanup always runs - Log API response time — surface slow endpoints in reports
MODULE 12
Framework Finalization & Interview Preparation
Full framework walkthrough · roles & responsibilities · project contribution · interview Q&A
Complete Framework Structure
tests/— test classes for GET, POST, PATCH, DELETE scenariosutils/— helper functions, response validators, data readersconfig/config.ini— environment configuration fileconftest.py— shared fixtures, base setup, teardown cleanuppytest.ini— marker registration, default run optionsreports/— Allure results and HTML report output folder
Interview Prep — Top Questions
How do you handle authentication in your framework?
Token is stored in
config.ini. A get_headers() helper in the base class reads and attaches Authorization: Bearer <token> to every request — no test manages auth directly.What is the difference between PUT and PATCH?
PUT replaces the entire resource — omitting a field clears it. PATCH updates only specified fields, leaving the rest unchanged. PATCH is safer for partial updates on GitHub's API.
How do you switch between environments?
Change
environment = staging to environment = production in config.ini. The base class reads this and loads the correct base_url and token automatically — zero code changes.What is conftest.py?
pytest's shared fixture file. Fixtures here are automatically available to all test files without any import. Auth headers, base URL, and teardown cleanup all live here.
How do you clean up data after tests?
Use
@pytest.fixture with yield — create the resource before yield, delete it after. This guarantees cleanup runs even if the test fails, keeping each run independent.What is your contribution to the framework?
Writing GET/POST/PATCH/DELETE automation scripts, building utility and helper functions, creating test data files, configuring
config.ini for multi-environment, and integrating Allure reporting.